package com.njwd.rpc.annotation;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.SystemPropertyUtils;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.registry.Registry;
import com.alibaba.dubbo.registry.RegistryFactory;

public class CrouteScan {

	private static final Logger log = LoggerFactory.getLogger(CrouteScan.class);
	// 扫描 scanPackages 下的文件的匹配符
	protected static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
	private String base = "com.njwd";
	private String zkpath;
	private long timeout = 3000l;

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void doRegister() {
		Set<Class> clz = findClassAnnotation(base);

		if (clz.size() == 0) {
			return;
		}
		RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(
				RegistryFactory.class).getAdaptiveExtension();
		Registry registry = registryFactory.getRegistry(URL
				.valueOf("zookeeper://" + zkpath + "?timeout=" + timeout));
		for (Class c : clz) {
			String interfaceName = c.getName();
			CRoute croute = (CRoute) c.getAnnotation(CRoute.class);
			String roteName = croute.routeName();
			StringBuffer sb = new StringBuffer("routers://0.0.0.0/")
					.append(interfaceName).append("?name=")
					.append(c.getSimpleName())
					.append("g&category=routers&router=")
					.append(roteName).append("&dynamic=false&runtime=true");
			registry.register(URL.valueOf(sb.toString()));
		}

	}

	@SuppressWarnings("all")
	public Set<Class> findClassAnnotation(String scanPackages) {
		// 获取所有的类
		Set<Class> clz = findPackageClass(scanPackages);
		for (Class c : clz) {
			checkMethod(c);
		}
		return clz;

	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Set<Class> findPackageClass(String scanPackages) {
		if (StringUtils.isBlank(scanPackages)) {
			throw new RuntimeException(" scanner packages is null");
		}

		ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
		MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(
				resourcePatternResolver);
		Set<Class> clazzSet = new HashSet<Class>();

		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
				+ org.springframework.util.ClassUtils
						.convertClassNameToResourcePath(SystemPropertyUtils
								.resolvePlaceholders(scanPackages))
				+ "/"
				+ DEFAULT_RESOURCE_PATTERN;
		try {
			Resource[] resources = resourcePatternResolver
					.getResources(packageSearchPath);
			for (Resource resource : resources) {
				// 检查resource，这里的resource都是class
				Class clazz =loadClassName(metadataReaderFactory, resource);
				if (clazz.isAnnotationPresent(CRoute.class)) {
					clazzSet.add(clazz);
				}

			}
		} catch (Exception e) {
			log.error("获取包下面的类信息失败,package:" + scanPackages, e);
		}

		return clazzSet;
	}

	@SuppressWarnings("all")
	private Class loadClassName(MetadataReaderFactory metadataReaderFactory,
			org.springframework.core.io.Resource resource) throws IOException {
		try {
			if (resource.isReadable()) {
				MetadataReader metadataReader = metadataReaderFactory
						.getMetadataReader(resource);
				if (metadataReader != null) {
					return Class.forName(metadataReader.getClassMetadata().getClassName());
				}
			}
		} catch (Exception e) {
			log.error("根据resource获取类名称失败", e);
		}
		return null;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void checkMethod(Class clz) {
		Method[] methods = clz.getDeclaredMethods();
		for (Method method : methods) {
			if (method.getModifiers() != Modifier.PUBLIC) {
				continue;
			}
			Class<?>[] mclz = method.getParameterTypes();
			if (mclz == null || mclz.length == 0) {
				throw new RuntimeException(
						" method is not group param! interface:"
								+ clz.getName() + ",method:" + method.getName());
			}
			Class mcl1 = mclz[0];
			if (!mcl1.isAssignableFrom(String.class)) {
				throw new RuntimeException(
						" The first parameter must be string:" + clz.getName()
								+ ",method:" + method.getName());
			}

		}
	}

	public String getBase() {
		return base;
	}

	public void setBase(String base) {
		this.base = base;
	}

	public String getZkpath() {
		return zkpath;
	}

	public void setZkpath(String zkpath) {
		this.zkpath = zkpath;
	}

	public long getTimeout() {
		return timeout;
	}

	public void setTimeout(long timeout) {
		this.timeout = timeout;
	}

}
